#region Using Statements
using System;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Runtime.Remoting;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
#endregion

namespace SkyBurner.AI
{
    // EnemyUnitBase class
    //
    #region EnemyUnitBase class
    public class EnemyUnitBase : BaseObject
    {
        protected Vector3 m_vPosition;
        protected Vector3 m_vDirection;
        protected BoundingBoxEx m_boundingBox;
        protected int m_nUnitIndex;
        protected float m_fLifeTime;
        protected float m_fEnergy;
        protected Matrix m_mxWorld;
        protected EnemyAIHandlerBase m_enemyAIHandlerBase;
        protected bool m_bShowEnergyBar;
        protected int m_nDamage;

        public Matrix WorldMatrix
        {
            get { return m_mxWorld; }
        }

        public Vector3 Position
        {
            get { return m_vPosition; }
        }

        public BoundingBoxEx BoundingBox
        {
            get { return m_boundingBox; }
        }

        public float Energy
        {
            get { return m_fEnergy; }
            set { m_fEnergy = value; }
        }

        public float LifeTime
        {
            get { return m_fLifeTime; }
        }

        public bool ShowEnergyBar
        {
            get { return m_bShowEnergyBar; }
        }

        public int Damage
        {
            get { return m_nDamage; }
        }

        public EnemyUnitBase()
            : base()
        {

        }

        public EnemyUnitBase(BaseGame pGame, EnemyAIHandlerBase pEnemyAIHandlerBase, int nUnitIndex, Vector3 vPosition, Vector3 vDirection, BoundingBoxEx pBoundingBoxEx, XMLPropertyBag pCfgPropertyBag)
            : base(pGame)
        {
            m_vPosition = vPosition;
            m_vDirection = vDirection;
            m_nUnitIndex = nUnitIndex;
            m_fLifeTime = 0.0f;
            m_fEnergy = 100.0f;
            m_enemyAIHandlerBase = pEnemyAIHandlerBase;
            m_bShowEnergyBar = true;
            m_nDamage = 10;

            m_boundingBox = new BoundingBoxEx(pBoundingBoxEx.Min, pBoundingBoxEx.Max);                        
        }

        public virtual void Initialize(BaseGame pGame, EnemyAIHandlerBase pEnemyAIHandlerBase, int nUnitIndex, StarFighter pPlayerStarFighter, BoundingBoxEx pBoundingBoxEx, XMLPropertyBag pCfgPropertyBag)
        {
            m_Game = pGame;
            m_nUnitIndex = nUnitIndex;
            m_fLifeTime = 0.0f;
            m_fEnergy = 100.0f;
            m_enemyAIHandlerBase = pEnemyAIHandlerBase;
            m_bShowEnergyBar = true;
            m_nDamage = 10;

            m_boundingBox = new BoundingBoxEx(pBoundingBoxEx.Min, pBoundingBoxEx.Max);
        }

        public virtual void Update(GameTime gameTime, StarFighter pPlayerStarFighter, SkyBurnerWorld pWorld)
        {
            m_boundingBox.Transform(Matrix.CreateTranslation(m_vPosition));
        }

        public virtual void OnCollision(SkyBurnerBulletBase pBullet)
        {

        }

        public virtual bool IsAlive()
        {
            return false;
        }
    }
    #endregion

    // EnemyAIHandlerBase class
    //
    #region EnemyAIHandlerBase class
    public abstract class EnemyAIHandlerBase : MarshalByRefObject
    {
        protected BaseGame m_Game;
        protected SkyBurnerWorld m_skyBurnerWorld;
        protected Collection<EnemyUnitBase> m_enemyUnitList = new Collection<EnemyUnitBase>();
        protected MOODModel m_enemyUnitModel;
        protected MOODModel m_energyBarModel;
        protected float m_fEnergyPosY;
        protected Vector4 m_fullEnergyColor = new Vector4(0.0f, 1.0f, 0.0f, 1.0f);
        protected Vector4 m_lowEnergyColor = new Vector4(1.0f, 0.0f, 0.0f, 1.0f);
        protected Matrix m_mxWorld;
        protected Matrix m_mxView;
        protected Matrix m_mxProjection;
        protected XMLPropertyBag m_cfgPropertyBag;
        protected String m_unitHandlerClassName;
        protected Vector3 m_vSunLightPosition;

        public Vector3 SunLightPosition
        {
            get { return m_vSunLightPosition; }
        }

        public SkyBurnerWorld SkyBurnerWorld
        {
            get { return m_skyBurnerWorld; }
        }

        public EnemyAIHandlerBase()
        {

        }

        public virtual void Initialize(BaseGame game, String pUnitHandlerClass, SkyBurnerWorld pSkyBurnerWorld, XmlNode pConfigNode)
        {
            m_Game = game;
            m_skyBurnerWorld = pSkyBurnerWorld;
            m_unitHandlerClassName = pUnitHandlerClass;

            m_cfgPropertyBag = new XMLPropertyBag(pConfigNode);
            m_energyBarModel = new MOODModel(m_Game, "Data\\Models\\quad.mood");

            // get sunlight position...
            BaseWorldLight sunLight = pSkyBurnerWorld.GetLight("SunLight");
            if (sunLight != null)
                m_vSunLightPosition = sunLight.Position;
        }

        public abstract void Start(Vector3 pPlayerPosition);
        public abstract void Update(GameTime gameTime, StarFighter pPlayerStarFighter, SkyBurnerWorld pWorld);
        public abstract void Draw(GameTime gameTime, Camera pActiveCamera);

        public virtual int GetEnemyUnitCount()
        {
            return m_enemyUnitList.Count;
        }

        public virtual EnemyUnitBase GetEnemyUnitAt(int pEnemyID)
        {
            return m_enemyUnitList[pEnemyID];
        }

        public abstract void EnemyUnitHandleCollision(int pEnemyID, SkyBurnerBulletBase pBullet);
        public abstract void EnemyUnitHandleCollisionWithPlayer(int pEnemyID, StarFighter pPlayerStarFighter);
        public abstract bool IsAlive();
    }
    #endregion

    // EnemyAIHandler_Generic classes
    //
    #region EnemyAIHandler_Generic class
    public class EnemyAIHandler_Generic : EnemyAIHandlerBase
    {
        private MOODBlinnPhongShader m_moodBlinnPhongShader;
        private int m_nUnitCount;
        private float m_fUnitSpawnInterval;
        private float m_fLastUnitSpawnTick;        

        public EnemyAIHandler_Generic()
        {

        }

        public override void Initialize(BaseGame game, String pUnitHandlerClass, SkyBurnerWorld pSkyBurnerWorld, XmlNode pConfigNode)
        {
            base.Initialize(game, pUnitHandlerClass, pSkyBurnerWorld, pConfigNode);
            
            m_enemyUnitModel = new MOODModel(game, (String)m_cfgPropertyBag.GetValue<String>("Model", XMLPropertyBagValueType.vtString));

            m_fEnergyPosY = m_enemyUnitModel.BoundingBox.Max.Y + 0.2f;
            m_moodBlinnPhongShader = (MOODBlinnPhongShader)m_Game.ShadersManager.GetShader("BlinnPhongShader");

            m_nUnitCount = m_cfgPropertyBag.GetValue<int>("UnitCount", XMLPropertyBagValueType.vtInteger);
            m_fUnitSpawnInterval = m_cfgPropertyBag.GetValue<float>("UnitSpawnInterval", XMLPropertyBagValueType.vtFloat);
        }

        public override void Start(Vector3 pPlayerPosition)
        {
            m_fLastUnitSpawnTick = 0.0f;
        }

        public override void Update(GameTime gameTime, StarFighter pPlayerStarFighter, SkyBurnerWorld pWorld)
        {
            // check if we must spawn an unit...
            if (m_nUnitCount > 0)
            {
                if (((float)gameTime.TotalGameTime.TotalMilliseconds - m_fLastUnitSpawnTick) > m_fUnitSpawnInterval)
                {
                    m_fLastUnitSpawnTick = (float)gameTime.TotalGameTime.TotalMilliseconds;

                    ObjectHandle objectHandle = Activator.CreateInstance(null, m_unitHandlerClassName);
                    EnemyUnitBase enemyUnit = (EnemyUnitBase)objectHandle.Unwrap();

                    enemyUnit.Initialize(m_Game,
                                         this,
                                         m_nUnitCount,
                                         pPlayerStarFighter,
                                         m_enemyUnitModel.BoundingBox,
                                         m_cfgPropertyBag);

                    m_enemyUnitList.Add(enemyUnit);

                    m_nUnitCount--;
                }
            }

            // update all alive unit...
            int i = 0;
            while (i < m_enemyUnitList.Count)
            {
                EnemyUnitBase enemyUnit = m_enemyUnitList[i];

                if (!enemyUnit.IsAlive())
                {
                    m_enemyUnitList.RemoveAt(i);
                    continue;
                }
                else
                    enemyUnit.Update(gameTime, pPlayerStarFighter, pWorld);

                i++;
            }
        }

        public override void Draw(GameTime gameTime, Camera pActiveCamera)
        {
            m_mxView = Matrix.CreateLookAt(pActiveCamera.Position, pActiveCamera.LookAt, pActiveCamera.Up);
            m_mxProjection = Matrix.CreatePerspectiveFieldOfView(pActiveCamera.FOV, pActiveCamera.AspectRatio, pActiveCamera.NearPlane, pActiveCamera.FarPlane);

            foreach (EnemyUnitBase enemyUnit in m_enemyUnitList)
            {
                // draw the enemy 
                m_enemyUnitModel.WorldMatrix = enemyUnit.WorldMatrix; //Matrix.CreateTranslation(enemyUnit.Position);
                m_enemyUnitModel.ViewMatrix = m_mxView;
                m_enemyUnitModel.ProjectionMatrix = m_mxProjection;

                m_moodBlinnPhongShader.Draw(gameTime, pActiveCamera, pActiveCamera.Position, enemyUnit.Position + m_vSunLightPosition, m_enemyUnitModel);

                if (enemyUnit.ShowEnergyBar)
                {
                    // draw energy bar
                    m_energyBarModel.WorldMatrix = Matrix.CreateScale(enemyUnit.Energy / 100.0f, 0.1f, 1.0f) *
                                                   Matrix.CreateTranslation(enemyUnit.Position.X, enemyUnit.Position.Y + m_fEnergyPosY, enemyUnit.Position.Z);
                    m_energyBarModel.ViewMatrix = m_mxView;
                    m_energyBarModel.ProjectionMatrix = m_mxProjection;

                    m_energyBarModel.Materials[0].Ambient = Vector4.Lerp(m_lowEnergyColor, m_fullEnergyColor, enemyUnit.Energy / 100.0f);
                    m_energyBarModel.Materials[0].Emissive = Vector4.Lerp(m_lowEnergyColor, m_fullEnergyColor, enemyUnit.Energy / 100.0f);
                    m_energyBarModel.Materials[0].Diffuse = Vector4.Lerp(m_lowEnergyColor, m_fullEnergyColor, enemyUnit.Energy / 100.0f);
                    m_energyBarModel.Materials[0].Specular = Vector4.Lerp(m_lowEnergyColor, m_fullEnergyColor, enemyUnit.Energy / 100.0f);

                    m_energyBarModel.Draw(gameTime, pActiveCamera);
                }
            }
        }

        protected void DestroyUnit(int pEnemyID)
        {
            ParticleEmitterBase explosionEmitter = m_skyBurnerWorld.ParticleSystemManager.CreateEmitter("Explosion");
            explosionEmitter.Position = m_enemyUnitList[pEnemyID].Position;

            // play explosion sound
            m_Game.SoundManager.PlaySound("Explosion3");

            // give score to player
            SkyBurnerGlobal.AddScore(100);

            // remove the unit
            m_enemyUnitList.RemoveAt(pEnemyID);
        }

        public override void EnemyUnitHandleCollision(int pEnemyID, SkyBurnerBulletBase pBullet)
        {
            // remove the unit and start exposion...
            m_enemyUnitList[pEnemyID].OnCollision(pBullet);
            if (m_enemyUnitList[pEnemyID].Energy > 0.0f)
                m_Game.SoundManager.PlaySound("Metal");
            else
                DestroyUnit(pEnemyID);
        }

        public override void EnemyUnitHandleCollisionWithPlayer(int pEnemyID, StarFighter pPlayerStarFighter)
        {
            DestroyUnit(pEnemyID);
        }

        public override bool IsAlive()
        {
            return ((m_nUnitCount > 0) || (m_enemyUnitList.Count > 0));
        }
    }
    #endregion

    // EnemyUnit_Simple
    //
    #region EnemyUnit_Simple
    public class EnemyUnit_Simple : EnemyUnitBase
    {
        private float m_fFireInterval;
        private float m_fFireRadius;
        private float m_fFireDamage;
        private float m_fFireSpeed;
        private float m_fFireLifeTime;
        private float m_fLastFireSeconds;
        private Vector3 m_vFirePositionOffset;
        private Vector3 m_vLaserColor = new Vector3(1.0f, 1.0f, 0.0f);
        private Texture2D m_bulletTexture;

        public EnemyUnit_Simple()
            : base()
        {

        }

        public override void Initialize(BaseGame pGame, EnemyAIHandlerBase pEnemyAIHandlerBase, int nUnitIndex, StarFighter pPlayerStarFighter, BoundingBoxEx pBoundingBoxEx, XMLPropertyBag pCfgPropertyBag)
        {
            base.Initialize(pGame, pEnemyAIHandlerBase, nUnitIndex, pPlayerStarFighter, pBoundingBoxEx, pCfgPropertyBag);

            m_fLastFireSeconds = 0.0f;
            m_fEnergy = 100.0f;

            // load configuration...
            m_fLifeTime = pCfgPropertyBag.GetValue<float>("LifeTime", XMLPropertyBagValueType.vtFloat);
            m_fFireInterval = pCfgPropertyBag.GetValue<float>("FireInterval", XMLPropertyBagValueType.vtFloat);
            m_fFireRadius = pCfgPropertyBag.GetValue<float>("FireRadius", XMLPropertyBagValueType.vtFloat);
            m_fFireDamage = pCfgPropertyBag.GetValue<float>("FireDamage", XMLPropertyBagValueType.vtFloat);
            m_fFireSpeed = pCfgPropertyBag.GetValue<float>("FireSpeed", XMLPropertyBagValueType.vtFloat);
            m_fFireLifeTime = pCfgPropertyBag.GetValue<float>("FireLifeTime", XMLPropertyBagValueType.vtFloat);

            m_vFirePositionOffset = new Vector3(0.0f, -0.25f, -m_fFireRadius - 1.5f);
            m_bulletTexture = m_Game.ContentManager.Load<Texture2D>("Data\\Textures\\bullet_sprite_02");

            Random rnd = new Random();
            if ((m_nUnitIndex % 2) == 0)
            {
                m_vPosition.X = pPlayerStarFighter.Position.X + (float)rnd.NextDouble() * 40.0f;
                m_vPosition.Y = pPlayerStarFighter.Position.Y - (float)rnd.NextDouble() * 0.01f;
            }
            else
            {
                m_vPosition.X = pPlayerStarFighter.Position.X - (float)rnd.NextDouble() * 40.0f;
                m_vPosition.Y = pPlayerStarFighter.Position.Y + (float)rnd.NextDouble() * 0.01f;
            }

            m_vPosition.Z = pPlayerStarFighter.Position.Z + 200.0f;
        }

        public override void Update(GameTime gameTime, StarFighter pPlayerStarFighter, SkyBurnerWorld pWorld)
        {
            base.Update(gameTime, pPlayerStarFighter, pWorld);

            // check life time...
            m_fLifeTime -= (float)gameTime.ElapsedGameTime.Milliseconds;
            if (m_fLifeTime > 0)
            {
                // check if we must fire...
                bool bMustFire = (pPlayerStarFighter.Position.X >= m_vPosition.X - 0.5f) && (pPlayerStarFighter.Position.X <= m_vPosition.X + 0.5f) &&
                                 (pPlayerStarFighter.Position.Y >= m_vPosition.Y - 0.2f) && (pPlayerStarFighter.Position.Y <= m_vPosition.Y + 0.5f);

                if ((pPlayerStarFighter.Status == StarFighterStatus.Alive) && bMustFire && (gameTime.TotalGameTime.TotalMilliseconds - m_fLastFireSeconds > m_fFireInterval))
                {
                    Vector3 vFireDirection = -Vector3.UnitZ;
                    Vector3 vFirePosition = m_vPosition + m_vFirePositionOffset;

                    pWorld.AddBullet(new SkyBurnerBulletSprite(m_Game, m_enemyAIHandlerBase.SkyBurnerWorld, m_bulletTexture, new Vector3(vFirePosition.X - 1.0f, vFirePosition.Y, vFirePosition.Z), vFireDirection, m_fFireSpeed, m_fFireLifeTime, m_fFireDamage, m_fFireRadius, m_vLaserColor), SkyBurnerWorld.BulletFrom.Enemy);
                    pWorld.AddBullet(new SkyBurnerBulletSprite(m_Game, m_enemyAIHandlerBase.SkyBurnerWorld, m_bulletTexture, new Vector3(vFirePosition.X + 1.0f, vFirePosition.Y, vFirePosition.Z), vFireDirection, m_fFireSpeed, m_fFireLifeTime, m_fFireDamage, m_fFireRadius, m_vLaserColor), SkyBurnerWorld.BulletFrom.Enemy);

                    m_fLastFireSeconds = (float)gameTime.TotalGameTime.TotalMilliseconds;
                }
            }

            m_mxWorld = Matrix.CreateTranslation(m_vPosition);
        }

        public override bool IsAlive()
        {
            return (m_fLifeTime > 0.0f);
        }

        public override void OnCollision(SkyBurnerBulletBase pBullet)
        {
            base.OnCollision(pBullet);

            // cut energy...
            m_fEnergy -= pBullet.Damage * 5.0f;
        }
    }
    #endregion

    // EnemyUnit_Spin
    //
    #region EnemyUnit_Spin class
    public class EnemyUnit_Spin : EnemyUnitBase
    {
        #region EnemyUnitStatus enum
        private enum EnemyUnitStatus
        {
            Stand,
            Spin,
            Escape,
            Dead
        }
        #endregion

        private Vector3 m_vRotationSpan;
        private float m_fRotationRadius;
        private float m_fMinDistanceFromPlayer;
        private float m_fEndTime;
        private float m_fFireRadius;
        private float m_fFireDamage;
        private float m_fFireSpeed;
        private float m_fFireLifeTime;
        private float m_fFireInterval;
        private float m_fUnitSpeed;
        private Vector3 m_vPivot;
        private EnemyUnitStatus m_currentStatus;
        private float m_fRotationStep;
        private float m_fLastTickFire;
        private float m_fVerticalMove;
        private float m_fStandMoveStep;
        private float m_fVerticalSpeed;
        private ContentManager m_contentManager;
        private int m_nFireFrequency;
        private Vector3 m_vFirePositionOffset;
        private Vector3 m_vLaserColor = new Vector3(1.0f, 1.0f, 0.0f);
        private Texture2D m_bulletTexture;

        public EnemyUnit_Spin()
            : base()
        {

        }

        public override void Initialize(BaseGame pGame, EnemyAIHandlerBase pEnemyAIHandlerBase, int nUnitIndex, StarFighter pPlayerStarFighter, BoundingBoxEx pBoundingBoxEx, XMLPropertyBag pCfgPropertyBag)
        {
            base.Initialize(pGame, pEnemyAIHandlerBase, nUnitIndex, pPlayerStarFighter, pBoundingBoxEx, pCfgPropertyBag);

            m_currentStatus = EnemyUnitStatus.Stand;
            m_fRotationStep = 0.0f;
            m_fStandMoveStep = 0.0f;
            m_contentManager = new ContentManager(m_Game.Services);
            m_fLastTickFire = 0;
            m_fEnergy = 100.0f;
            m_fVerticalMove = 0.0f;

            Random rnd = new Random(nUnitIndex);
            m_nFireFrequency = rnd.Next(2) + 2;

            // load configuration...
            m_vRotationSpan = pCfgPropertyBag.GetValue<Vector3>("RotationSpan", XMLPropertyBagValueType.vtVector3);
            m_fRotationRadius = pCfgPropertyBag.GetValue<float>("RotationRadius", XMLPropertyBagValueType.vtFloat);
            m_fMinDistanceFromPlayer = pCfgPropertyBag.GetValue<float>("MinDistanceFromPlayer", XMLPropertyBagValueType.vtFloat);
            m_fEndTime = pCfgPropertyBag.GetValue<float>("EndTime", XMLPropertyBagValueType.vtFloat);
            m_fFireRadius = pCfgPropertyBag.GetValue<float>("FireRadius", XMLPropertyBagValueType.vtFloat);
            m_fFireDamage = pCfgPropertyBag.GetValue<float>("FireDamage", XMLPropertyBagValueType.vtFloat);
            m_fFireSpeed = pCfgPropertyBag.GetValue<float>("FireSpeed", XMLPropertyBagValueType.vtFloat);
            m_fFireLifeTime = pCfgPropertyBag.GetValue<float>("FireLifeTime", XMLPropertyBagValueType.vtFloat);
            m_fFireInterval = pCfgPropertyBag.GetValue<float>("FireInterval", XMLPropertyBagValueType.vtFloat);
            m_fUnitSpeed = pCfgPropertyBag.GetValue<float>("UnitSpeed", XMLPropertyBagValueType.vtFloat);
            m_fVerticalSpeed = pCfgPropertyBag.GetValue<float>("UnitVerticalSpeed", XMLPropertyBagValueType.vtFloat);

            m_vFirePositionOffset = new Vector3(0.0f, -0.25f, -m_fFireRadius - 2.0f);
            m_bulletTexture = m_Game.ContentManager.Load<Texture2D>("Data\\Textures\\bullet_sprite_02");

            m_vPosition = new Vector3(0.0f, pPlayerStarFighter.Position.Y, pPlayerStarFighter.Position.Z + 200.0f);
            m_vDirection = -Vector3.UnitZ;
        }

        public override void Update(GameTime gameTime, StarFighter pPlayerStarFighter, SkyBurnerWorld pWorld)
        {
            base.Update(gameTime, pPlayerStarFighter, pWorld);

            switch (m_currentStatus)
            {
                case EnemyUnitStatus.Stand:
                    {
                        float fDistance = Vector3.Distance(pPlayerStarFighter.Position, m_vPosition);

                        m_vPosition.X = 4.0f * (float)Math.Sin(m_fStandMoveStep);
                        m_vPosition.Y = 1.5f * (float)Math.Cos(m_fStandMoveStep);
                        m_vPosition.Z -= m_fUnitSpeed * (float)gameTime.ElapsedGameTime.Milliseconds;

                        m_fStandMoveStep += 0.08f;

                        if (fDistance <= m_fMinDistanceFromPlayer + m_fRotationRadius)
                        {
                            m_currentStatus = EnemyUnitStatus.Spin;
                            m_vPosition.Z = pPlayerStarFighter.Position.Z + m_fMinDistanceFromPlayer;

                            m_vPivot = m_vPosition;
                        }
                        break;
                    }

                case EnemyUnitStatus.Spin:
                    {
                        Vector3 vRotationCenter = pPlayerStarFighter.Position;

                        vRotationCenter.X = m_vPivot.X;
                        vRotationCenter.Y = m_vPivot.Y;
                        vRotationCenter.Z = pPlayerStarFighter.Position.Z + m_fMinDistanceFromPlayer + m_fRotationRadius;

                        Matrix mxRotation = Matrix.CreateRotationY(m_fRotationStep);
                        Vector3 vRotated = Vector3.Transform(-Vector3.UnitZ, mxRotation);

                        m_vPosition.X = vRotationCenter.X + m_fRotationRadius * vRotated.X;
                        m_vPosition.Y = vRotationCenter.Y + m_fVerticalMove * (float)gameTime.ElapsedGameTime.Milliseconds;
                        m_vPosition.Z = vRotationCenter.Z + m_fRotationRadius * vRotated.Z;

                        m_fRotationStep += 0.02f;
                        m_fRotationRadius += 0.01f;
                        m_fVerticalMove += 0.001f;

                        if ((pPlayerStarFighter.Status == StarFighterStatus.Alive) && ((gameTime.TotalGameTime.TotalMilliseconds - m_fLastTickFire) > m_fFireInterval))
                        {
                            Vector3 vFireDirection = -Vector3.UnitZ;
                            Vector3 vFirePosition = m_vPosition + m_vFirePositionOffset;

                            pWorld.AddBullet(new SkyBurnerBulletSprite(m_Game, m_enemyAIHandlerBase.SkyBurnerWorld, m_bulletTexture, new Vector3(vFirePosition.X - 1.0f, vFirePosition.Y, vFirePosition.Z), vFireDirection, m_fFireSpeed, m_fFireLifeTime, m_fFireDamage, m_fFireRadius, m_vLaserColor), SkyBurnerWorld.BulletFrom.Enemy);
                            pWorld.AddBullet(new SkyBurnerBulletSprite(m_Game, m_enemyAIHandlerBase.SkyBurnerWorld, m_bulletTexture, new Vector3(vFirePosition.X + 1.0f, vFirePosition.Y, vFirePosition.Z), vFireDirection, m_fFireSpeed, m_fFireLifeTime, m_fFireDamage, m_fFireRadius, m_vLaserColor), SkyBurnerWorld.BulletFrom.Enemy);

                            m_fLastTickFire = (float)gameTime.TotalGameTime.TotalMilliseconds;
                        }

                        if (m_vPosition.Y >= 5.0f)
                            m_currentStatus = EnemyUnitStatus.Escape;

                        break;
                    }

                case EnemyUnitStatus.Escape:
                    {
                        if (m_vPosition.Z < (pPlayerStarFighter.Position.Z - 50.0f))
                            m_currentStatus = EnemyUnitStatus.Dead;

                        break;
                    }
            }

            m_mxWorld = Matrix.CreateTranslation(m_vPosition);
        }

        public override bool IsAlive()
        {
            return (m_currentStatus != EnemyUnitStatus.Dead);
        }

        public override void OnCollision(SkyBurnerBulletBase pBullet)
        {
            base.OnCollision(pBullet);

            // cut energy...
            m_fEnergy -= pBullet.Damage * 5.0f;
        }
    }
    #endregion

    // EnemyUnit_Seeker
    //
    #region EnemyUnit_Seeker
    public class EnemyUnit_Seeker : EnemyUnitBase
    {
        #region EnemyUnitStatus enum
        private enum EnemyUnitStatus
        {
            Stand,
            Move,
            Escape,
            Dead
        }
        #endregion

        private float m_fPlayerPositionCheckInterval;
        private float m_fMinDistanceFromPlayer;
        private float m_fUnitSpeed;
        private float m_fFireInterval;
        private float m_fFireRadius;
        private float m_fFireDamage;
        private float m_fFireSpeed;
        private float m_fFireLifeTime;
        private EnemyUnitStatus m_currentStatus;
        private float m_fInitialDistance;
        private Vector3 m_vLastPlayerPosition;
        private float m_fLastTickMove;
        private float m_fLastTickFire;
        private float m_fLerp;
        private Vector3 m_vFirePositionOffset;
        private Vector3 m_vLaserColor = new Vector3(1.0f, 0.2f, 0.0f);
        private float m_fRotation;
        private Texture2D m_bulletTexture;

        public float Rotation
        {
            get { return m_fRotation; }
        }

        public EnemyUnit_Seeker()
            : base()
        {

        }

        public override void Initialize(BaseGame pGame, EnemyAIHandlerBase pEnemyAIHandlerBase, int nUnitIndex, StarFighter pPlayerStarFighter, BoundingBoxEx pBoundingBoxEx, XMLPropertyBag pCfgPropertyBag)
        {
            base.Initialize(pGame, pEnemyAIHandlerBase, nUnitIndex, pPlayerStarFighter, pBoundingBoxEx, pCfgPropertyBag);

            m_currentStatus = EnemyUnitStatus.Stand;

            m_fLastTickMove = 0.0f;
            m_fLastTickFire = 0.0f;
            m_fRotation = 0.0f;

            // load configuration
            m_fPlayerPositionCheckInterval = pCfgPropertyBag.GetValue<float>("PlayerPositionCheckInterval", XMLPropertyBagValueType.vtFloat);
            m_fMinDistanceFromPlayer = pCfgPropertyBag.GetValue<float>("MinDistanceFromPlayer", XMLPropertyBagValueType.vtFloat);
            m_fUnitSpeed = pCfgPropertyBag.GetValue<float>("UnitSpeed", XMLPropertyBagValueType.vtFloat);
            m_fFireInterval = pCfgPropertyBag.GetValue<float>("FireInterval", XMLPropertyBagValueType.vtFloat);
            m_fFireRadius = pCfgPropertyBag.GetValue<float>("FireRadius", XMLPropertyBagValueType.vtFloat);
            m_fFireDamage = pCfgPropertyBag.GetValue<float>("FireDamage", XMLPropertyBagValueType.vtFloat);
            m_fFireSpeed = pCfgPropertyBag.GetValue<float>("FireSpeed", XMLPropertyBagValueType.vtFloat);
            m_fFireLifeTime = pCfgPropertyBag.GetValue<float>("FireLifeTime", XMLPropertyBagValueType.vtFloat);

            m_vFirePositionOffset = new Vector3(0.0f, -0.0f, -m_fFireRadius - 3.0f);
            m_bulletTexture = m_Game.ContentManager.Load<Texture2D>("Data\\Textures\\bullet_sprite_01");

            m_vPosition = new Vector3(pPlayerStarFighter.Position.X, pPlayerStarFighter.Position.Y, pPlayerStarFighter.Position.Z + 200.0f);
            m_vDirection = -Vector3.UnitZ;
        }

        public override void Update(GameTime gameTime, StarFighter pPlayerStarFighter, SkyBurnerWorld pWorld)
        {
            base.Update(gameTime, pPlayerStarFighter, pWorld);

            // sample player position...
            if ((gameTime.TotalGameTime.TotalMilliseconds - m_fLastTickMove) > m_fPlayerPositionCheckInterval)
            {
                m_vLastPlayerPosition = pPlayerStarFighter.Position;
                m_fLastTickMove = (float)gameTime.TotalGameTime.TotalMilliseconds;
            }

            switch (m_currentStatus)
            {
                case EnemyUnitStatus.Stand:
                    {
                        m_fInitialDistance = m_vPosition.Z - pPlayerStarFighter.Position.Z;

                        m_vPosition.Z -= m_fUnitSpeed * (float)gameTime.TotalGameTime.TotalMilliseconds;
                        if ((m_vPosition.Z - pPlayerStarFighter.Position.Z) <= m_fMinDistanceFromPlayer)
                        {
                            m_vPosition.Z = pPlayerStarFighter.Position.Z + m_fMinDistanceFromPlayer;
                            m_currentStatus = EnemyUnitStatus.Move;
                        }

                        break;
                    }

                case EnemyUnitStatus.Move:
                    {
                        Vector3 vNewPostion;
                        float fDistanceZ;

                        fDistanceZ = (m_vPosition.Z - pPlayerStarFighter.Position.Z);

                        m_fLerp = ((m_fLastTickMove + m_fMinDistanceFromPlayer) - (float)gameTime.TotalGameTime.TotalMilliseconds) / m_fMinDistanceFromPlayer * 0.1f;
                        vNewPostion = Vector3.Lerp(m_vPosition, m_vLastPlayerPosition, m_fLerp);

                        m_vPosition.X = vNewPostion.X;
                        m_vPosition.Y = vNewPostion.Y;

                        m_fRotation = (m_vPosition.X - pPlayerStarFighter.Position.X) * 0.015f;

                        if (fDistanceZ <= m_fMinDistanceFromPlayer)
                            m_vPosition.Z = pPlayerStarFighter.Position.Z + m_fMinDistanceFromPlayer;

                        // check if we need to fire... 
                        if ((pPlayerStarFighter.Status == StarFighterStatus.Alive) && ((gameTime.TotalGameTime.TotalMilliseconds - m_fLastTickFire) > m_fFireInterval))
                        {
                            Vector3 vFireDirection = -Vector3.UnitZ;
                            Vector3 vFirePosition = m_vPosition + m_vFirePositionOffset;

                            pWorld.AddBullet(new SkyBurnerBulletSprite(m_Game, m_enemyAIHandlerBase.SkyBurnerWorld, m_bulletTexture, new Vector3(vFirePosition.X - 0.7f, vFirePosition.Y, vFirePosition.Z), vFireDirection, m_fFireSpeed, m_fFireLifeTime, m_fFireDamage, m_fFireRadius, m_vLaserColor), SkyBurnerWorld.BulletFrom.Enemy);
                            pWorld.AddBullet(new SkyBurnerBulletSprite(m_Game, m_enemyAIHandlerBase.SkyBurnerWorld, m_bulletTexture, new Vector3(vFirePosition.X + 0.7f, vFirePosition.Y, vFirePosition.Z), vFireDirection, m_fFireSpeed, m_fFireLifeTime, m_fFireDamage, m_fFireRadius, m_vLaserColor), SkyBurnerWorld.BulletFrom.Enemy);

                            m_fLastTickFire = (float)gameTime.TotalGameTime.TotalMilliseconds;
                        }

                        break;
                    }

                case EnemyUnitStatus.Dead:
                    {
                        break;
                    }
            }

            m_mxWorld = Matrix.CreateRotationZ(m_fRotation) * Matrix.CreateTranslation(m_vPosition);
        }

        public override bool IsAlive()
        {
            return (m_currentStatus != EnemyUnitStatus.Dead);
        }

        public override void OnCollision(SkyBurnerBulletBase pBullet)
        {
            base.OnCollision(pBullet);

            // cut energy...
            m_fEnergy -= pBullet.Damage * 0.25f;
        }
    }
    #endregion

    // EnemyUnit_Bomber
    //
    #region EnemyUnit_Bomber
    public class EnemyUnit_Bomber : EnemyUnitBase
    {
        private float m_fFireInterval;
        private float m_fFireRadius;
        private float m_fFireDamage;
        private float m_fFireSpeed;
        private float m_fFireLifeTime;
        private float m_fLastFireSeconds;
        private Vector3 m_vFirePositionOffset;
        private Vector3 m_vLaserColor = new Vector3(1.0f, 1.0f, 0.0f);

        public EnemyUnit_Bomber()
            : base()
        {

        }

        public override void Initialize(BaseGame pGame, EnemyAIHandlerBase pEnemyAIHandlerBase, int nUnitIndex, StarFighter pPlayerStarFighter, BoundingBoxEx pBoundingBoxEx, XMLPropertyBag pCfgPropertyBag)
        {
            base.Initialize(pGame, pEnemyAIHandlerBase, nUnitIndex, pPlayerStarFighter, pBoundingBoxEx, pCfgPropertyBag);

            m_fLastFireSeconds = 0.0f;
            m_fEnergy = 100.0f;

            // load configuration...
            m_fLifeTime = pCfgPropertyBag.GetValue<float>("LifeTime", XMLPropertyBagValueType.vtFloat );
            m_fFireInterval = pCfgPropertyBag.GetValue<float>("FireInterval", XMLPropertyBagValueType.vtFloat);
            m_fFireRadius = pCfgPropertyBag.GetValue<float>("FireRadius", XMLPropertyBagValueType.vtFloat);
            m_fFireDamage = pCfgPropertyBag.GetValue<float>("FireDamage", XMLPropertyBagValueType.vtFloat);
            m_fFireSpeed = pCfgPropertyBag.GetValue<float>("FireSpeed", XMLPropertyBagValueType.vtFloat);
            m_fFireLifeTime = pCfgPropertyBag.GetValue<float>("FireLifeTime", XMLPropertyBagValueType.vtFloat);

            m_vFirePositionOffset = new Vector3(0.0f, -0.25f, -m_fFireRadius - 13.0f);

            Random rnd = new Random();
            if ((m_nUnitIndex % 2) == 0)
            {
                m_vPosition.X = pPlayerStarFighter.Position.X + (float)rnd.NextDouble() * 40.0f;
                m_vPosition.Y = pPlayerStarFighter.Position.Y - (float)rnd.NextDouble() * 0.01f;
            }
            else
            {
                m_vPosition.X = pPlayerStarFighter.Position.X - (float)rnd.NextDouble() * 40.0f;
                m_vPosition.Y = pPlayerStarFighter.Position.Y + (float)rnd.NextDouble() * 0.01f;
            }

            m_vPosition.Z = pPlayerStarFighter.Position.Z + 500.0f;
        }

        protected void OnFireRocket(Vector3 pPosition)
        {
            ParticleEmitterBase rocketSmokeEmitter = m_enemyAIHandlerBase.SkyBurnerWorld.ParticleSystemManager.CreateEmitter("EnemyRocketSmoke");
            m_enemyAIHandlerBase.SkyBurnerWorld.AddBullet(new SkyBurnerRocketBulletPlayerTarget(m_Game,
                                                                                                 m_enemyAIHandlerBase.SkyBurnerWorld,
                                                                                                 m_enemyAIHandlerBase.SunLightPosition,
                                                                                                 Matrix.CreateRotationY(MathHelper.Pi),
                                                                                                 pPosition,
                                                                                                 -Vector3.UnitZ,
                                                                                                 m_fFireSpeed,
                                                                                                 m_fFireLifeTime,
                                                                                                 m_fFireDamage,
                                                                                                 m_fFireRadius,
                                                                                                 rocketSmokeEmitter), SkyBurnerWorld.BulletFrom.Enemy);
        }

        public override void Update(GameTime gameTime, StarFighter pPlayerStarFighter, SkyBurnerWorld pWorld)
        {
            base.Update(gameTime, pPlayerStarFighter, pWorld);

            // check life time...
            m_fLifeTime -= (float)gameTime.ElapsedGameTime.Milliseconds;
            if (m_fLifeTime > 0)
            {
                float fDistanceFromPlayer = m_vPosition.Z - pPlayerStarFighter.Position.Z;

                if ((pPlayerStarFighter.Status == StarFighterStatus.Alive) && (fDistanceFromPlayer > 200.0f) && (gameTime.TotalGameTime.TotalMilliseconds - m_fLastFireSeconds > m_fFireInterval))
                {
                    Vector3 vFireDirection = -Vector3.UnitZ;
                    Vector3 vFirePosition = m_vPosition + m_vFirePositionOffset;

                    OnFireRocket(vFirePosition);

                    m_fLastFireSeconds = (float)gameTime.TotalGameTime.TotalMilliseconds;
                }
            }

            m_mxWorld = Matrix.CreateTranslation(m_vPosition);
        }

        public override bool IsAlive()
        {
            return (m_fLifeTime > 0.0f);
        }

        public override void OnCollision(SkyBurnerBulletBase pBullet)
        {
            base.OnCollision(pBullet);

            // cut energy...
            m_fEnergy -= pBullet.Damage * 2.0f;
        }
    }
    #endregion

    // EnemyUnit_Asteroid
    //
    #region EnemyUnit_Asteroid
    public class EnemyUnit_Asteroid : EnemyUnitBase
    {
        private float m_fSpeed;
        private float m_fRotationSpeed;
        private Matrix m_mxRotation;
        private Vector3 m_vRotationAxis;
        private float m_fRotationAngle;

        public EnemyUnit_Asteroid()
            : base()
        {
            
        }

        public override void Initialize(BaseGame pGame, EnemyAIHandlerBase pEnemyAIHandlerBase, int nUnitIndex, StarFighter pPlayerStarFighter, BoundingBoxEx pBoundingBoxEx, XMLPropertyBag pCfgPropertyBag)
        {
            base.Initialize(pGame, pEnemyAIHandlerBase, nUnitIndex, pPlayerStarFighter, pBoundingBoxEx, pCfgPropertyBag);

            // we don't show energy for asteroid...
            m_bShowEnergyBar = false;

            // load configuration...
            m_fLifeTime = pCfgPropertyBag.GetValue<float>("LifeTime", XMLPropertyBagValueType.vtFloat);
            m_fSpeed = pCfgPropertyBag.GetValue<float>("Speed", XMLPropertyBagValueType.vtFloat);
            m_fRotationSpeed = pCfgPropertyBag.GetValue<float>("RotationSpeed", XMLPropertyBagValueType.vtFloat);

            Random rnd = new Random(nUnitIndex);
            
            m_vPosition = new Vector3(50.0f - (100.0f * (float)rnd.NextDouble()), 3.0f - (1.5f * (float)rnd.NextDouble()), pPlayerStarFighter.Position.Z + 200.0f);
            m_vDirection = pPlayerStarFighter.Position - m_vPosition;
            m_vDirection.Normalize();

            m_vRotationAxis = Vector3.Cross(m_vDirection, Vector3.Up);
            m_vRotationAxis.Normalize();

            m_fRotationAngle = 0.0f;
            m_fRotationSpeed *= (float)rnd.NextDouble();
        }

        public override void Update(GameTime gameTime, StarFighter pPlayerStarFighter, SkyBurnerWorld pWorld)
        {
            // update life time...
            m_fLifeTime -= (float)gameTime.ElapsedGameTime.Milliseconds;
            if (m_fLifeTime > 0.0f)
            {
                // update position an spin...
                m_vPosition += (m_fSpeed * (float)gameTime.ElapsedGameTime.Milliseconds) * m_vDirection;

                m_fRotationAngle -= (m_fRotationSpeed * (float)gameTime.ElapsedGameTime.Milliseconds);
                m_mxRotation = Matrix.CreateFromAxisAngle(m_vRotationAxis, m_fRotationAngle);
            }

            // setup world matrix...
            m_mxWorld = m_mxRotation * Matrix.CreateTranslation(m_vPosition);

            base.Update(gameTime, pPlayerStarFighter, pWorld);
        }

        public override bool IsAlive()
        {
            return (m_fLifeTime > 0.0f);
        }
    }
    #endregion
}
